package com.example.ssyinitlearn.webController;

import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;

@Controller
public class IndexController {

    /**
     * test1--hello world
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/test1")
    public String test1() {
        return "hello ssy";
    }

    /**
     * test2--login success
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/test2")
    public String test2() {
        return "hello world";
    }

    @ResponseBody
    @GetMapping("/test12")
    public String test12() {
        return "hello nn";
    }

    @GetMapping("/login1.html")
    public String login1_html() {
        return "login1.html";
    }

    @GetMapping("/login2.html")
    public String login2_html() {
        return "login2.html";
    }

    @GetMapping("/rmbMe1.html")
    public String rmbMe1_html(){
        return "rmbMe1.html";
    }

    /**
     * test3_getUserInfo
     * 从 SecurityContextHolder 中获取 Authentication对象
     *
     * @return
     */
    @ResponseBody
    @GetMapping("test3_getUserInfo")
    public HashMap<String, Object> getUserInfo() {
        HashMap<String, Object> map = new HashMap<>();
        // 从 SecurityContextHolder中获取当前线程登录用户的 Authentication
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 然后就正常调用 Authentication中的方法获取用户对象的相关内容
        String name = authentication.getName();
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        map.put("name", name);
        map.put("authorities", authorities);
        return map;
    }

    /**
     * test4_getSubThreadUserInfo
     * 从 SecurityContextHolder 中获取 Authentication对象
     *
     * @return
     */
    @ResponseBody
    @GetMapping("test4_getSubThreadUserInfo")
    public String getSubThreadUserInfo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String name = authentication.getName();
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        System.out.println("parentThreadName: " + name);
        System.out.println("parentThreadAuthorities: " + authorities);

        // 如果不做任何操作，那么在子线程中是无法获得到父线程中的Authentication的
        new Thread(new Runnable() {
            @Override
            public void run() {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication != null) {
                    String name = authentication.getName();
                    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                    System.out.println(Thread.currentThread().getId() + " subThreadName: " + name);
                    System.out.println(Thread.currentThread().getId() + " subThreadAuthorities: " + authorities);
                } else {
                    System.out.println("子线程" + Thread.currentThread().getId() + "获取Authentication失败");
                }
            }
        }).start();

        // 获取父线程中的 SecurityContext
        SecurityContext context = SecurityContextHolder.getContext();

        // 如果想要在子线程中同样也能在任何地方获取Authentication，那么就需要强制同步
        new Thread(new Runnable() {
            @Override
            public void run() {
                SecurityContextHolder.setContext(context);
                // 强制把父线程中的 SecurityContext 同步到子线程中
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication != null) {
                    String name = authentication.getName();
                    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                    System.out.println(Thread.currentThread().getId() + " subThreadName: " + name);
                    System.out.println(Thread.currentThread().getId() + " subThreadAuthorities: " + authorities);
                } else {
                    System.out.println("子线程" + Thread.currentThread().getId() + "获取Authentication失败");
                }
            }
        }).start();

        return "OK";
    }

    /**
     * 直接从请求头中获取登录用户信息
     * @param authentication
     * @return
     */
    @ResponseBody
    @GetMapping("test5_getObjectUserInfo")
    public String getObjectUserInfo(HttpServletRequest httpServletRequest, Authentication authentication){
        // 直接在方法头中映射参数
        System.out.println(authentication);

        // httpServletRequest是被SSY封装后的类，调用 HttpServletRequest原生的安全认证方法，也可以获得SSY内的认证对象
        String remoteUser = httpServletRequest.getRemoteUser();// 获取认证用户
        Authentication principal =  (Authentication) httpServletRequest.getUserPrincipal();  // 获取principal(Authentication子类)
        boolean admin = httpServletRequest.isUserInRole("admin");  // 判断是否具备权限
        System.out.println(remoteUser);
        System.out.println(admin);
        System.out.println(authentication.equals(principal));  // 直接从请求头中映射的 Authentication，和从HttpServletRequest中获取到的Authentication实际上是完全相同的对象

        return "OK";
    }

    /**
     * 验证码接口
     * 生成验证码文本并存入HttpSession中，然后生成验证码图片，交给前端显示
     */
    @Autowired
    Producer producer;
    @ResponseBody
    @GetMapping("/vcode.jpg")
    public void getVerifyCode(HttpServletResponse resp, HttpSession session) throws IOException {
        resp.setContentType("image/jpeg");
        String text = producer.createText();
        // 注意这里向session中加入了验证码，当登录页面向 /doLogin1 发送登录form时也会携带这个session
        session.setAttribute("kaptcha", text);
        BufferedImage image = producer.createImage(text);
        try(ServletOutputStream out = resp.getOutputStream()) {
            ImageIO.write(image, "jpg", out);
        }
    }

    /**
     * 认证后才可以访问，无论通过何种认证方式
     * @return
     */
    @ResponseBody
    @GetMapping("/remember/hello")
    public String rememberHello(){
        return "hello, remember";
    }

    /**
     * 认证后才可以访问，且不允许是 rmbMe模式
     * @return
     */
    @ResponseBody
    @GetMapping("/remember/protected")
    public String rememberProtected(){
        return "protected, remember";
    }

}
